package com.leetcode.ask;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ProjectName: leetcode
 * @Package: com.leetcode.ask
 * @description: 两数之和
 * 第1题
 * 给定一个整数数组 nums 和一个目标值 target，请你在该数组中找出和为目标值的那 两个 整数，并返回他们的数组下标。
 * 你可以假设每种输入只会对应一个答案。但是，你不能重复利用这个数组中同样的元素。
 * 示例:
 * 给定 nums = [2, 7, 11, 15], target = 9
 * 因为 nums[0] + nums[1] = 2 + 7 = 9
 * 所以返回 [0, 1]
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/two-sum
 * @Author: liyc
 * @CreateDate: 2020/1/2 3:28 PM
 * @Version: 1.0
 * <p>Copyright: Copyright (c) 2020</p>
 */
public class TwoSum {


    /**
     * @param
     * @return
     * @description 一遍哈希表
     * @author liyc
     * @date 2020/1/6
     */
    public static int[] officalByOne(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int n = target - nums[i];
            if (map.containsKey(n) && i != map.get(n)) {
                return new int[]{map.get(n), i};
            }
            map.put(nums[i], i);
        }
        throw new IllegalArgumentException("param error");

    }


    /**
     * @param
     * @return
     * @description 两遍哈希表
     * 为了对运行时间复杂度进行优化，我们需要一种更有效的方法来检查数组中是否存在目标元素。如果存在，我们需要找出它的索引。保持数组中的每个元素与其索引相互对应的最好方法是什么？哈希表。
     * <p>
     * 通过以空间换取速度的方式，我们可以将查找时间从 O(n)O(n) 降低到 O(1)O(1)。哈希表正是为此目的而构建的，它支持以 近似 恒定的时间进行快速查找。我用“近似”来描述，是因为一旦出现冲突，查找用时可能会退化到 O(n)O(n)。但只要你仔细地挑选哈希函数，在哈希表中进行查找的用时应当被摊销为 O(1)O(1)。
     * <p>
     * 一个简单的实现使用了两次迭代。在第一次迭代中，我们将每个元素的值和它的索引添加到表中。然后，在第二次迭代中，我们将检查每个元素所对应的目标元素（target - nums[i]target−nums[i]）是否存在于表中。注意，该目标元素不能是 nums[i]nums[i] 本身！
     * <p>
     * 链接：https://leetcode-cn.com/problems/two-sum/solution/liang-shu-zhi-he-by-leetcode-2/
     * 复杂度分析：
     * 时间复杂度：O(n)O(n)，
     * 我们把包含有 nn 个元素的列表遍历两次。由于哈希表将查找时间缩短到 O(1)O(1) ，所以时间复杂度为 O(n)O(n)。
     * 空间复杂度：O(n)O(n)，
     * 所需的额外空间取决于哈希表中存储的元素数量，该表中存储了 nn 个元素。
     * @author liyc
     * @date 2020/1/6
     */
    public static int[] offical(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; i++) {
            int m = nums[i];
            map.put(m, i);
        }
        for (int i = 0; i < nums.length; i++) {
            int n = target - nums[i];

            if (null != map.get(n) && i != map.get(n)) {
                return new int[]{i, map.get(n)};
            }
        }
        throw new IllegalArgumentException("param error");

    }

    /**
     * @param
     * @return
     * @description nums = [2, 7, 11, 15], target = 9
     * 暴力法
     * 循环方法，先用target减去所有数组里面的数据，
     * 得到一个新的数组，判断两个数组有没有相同的数据，如果有则找到了对应的值
     * 时间复杂度：O(n^2)O(n2)，
     * 对于每个元素，我们试图通过遍历数组的其余部分来寻找它所对应的目标元素，这将耗费 O(n)O(n) 的时间。因此时间复杂度为 O(n^2)O(n
     * 2
     * )。
     * 空间复杂度：O(1)O(1)。
     * https://leetcode-cn.com/problems/two-sum/solution/liang-shu-zhi-he-by-leetcode-2/
     * @author liyc
     * @date 2020/1/2
     */
    public static int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap();
        for (int i = 0; i < nums.length; i++) {
            for (int j = 0; j < nums.length; j++) {
                if (i == j) {
                    continue;
                }
                if (nums[i] + nums[j] == target) {
                    map.put(i, i);
                    map.put(j, j);
                }
            }
        }

        List<Integer> list = new ArrayList();
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            list.add(entry.getValue());
        }
        int []m = new int[list.size()];
        for(int i=0;i<list.size();i++){
            m[i]=list.get(i);
        }
        return m;
    }

    public static void main(String[] args) {
        int []arr = new int[]{3,2,4};
        int []ret = twoSum(arr,6);
        for (int i = 0; i < ret.length; i++) {
            System.out.println(ret[i]);
        }
    }
}
